//
//  UMPView.m
//  UMPDemo
//
//  Created by Jonathan Diehl on 01.03.10.
//  Copyright 2010 RWTH. All rights reserved.
//

#include <math.h>
#import "UMPListener.h"
#import "UMPTouch.h"
#import "ump.h"

#import "UMPView.h"

#define GRID_COLOR p, p, p, 1.0

#define TOUCH_CROSS_SIZE 3.0
#define TOUCH_CROSS_COLOR 1.0, 0.0, 0.0, 1.0

#define TOUCH_PREVIOUS_LINE_COLOR 0.75, 0.0, 0.0, 1.0

#define TOUCH_PRESSURE_OFFSET 10.0
#define TOUCH_PRESSURE_WIDTH 8.0
#define TOUCH_PRESSURE_HEIGHT 25.0
#define TOUCH_PRESSURE_COLOR 0.0, 0.0, 1.0, 1.0
#define TOUCH_PRESSURE_BGCOLOR 0.0, 0.0, 0.25, 1.0

#define TOUCH_TRAIL_COLOR 0.8, 0.8, 0.8, 1.0

@interface UMPView ()
- (void)drawGridInRect:(NSRect)rect context:(CGContextRef)c;
- (void)drawTouchesInRect:(NSRect)rect context:(CGContextRef)c;
- (void)drawTouchTrailsInRect:(NSRect)rect context:(CGContextRef)c;
@end


@implementation UMPView

@synthesize umpListener;
@synthesize touches;
@synthesize showGrid, showTouches, showTouchesPressure , showTouchTrail, showPreviousTouch;

// init
- (void)awakeFromNib;
{
	showGrid = YES;
	showTouches = YES;
	showTouchesPressure = NO;
	showTouchTrail = YES;
	showPreviousTouch = YES;
	
	touchTrails = [[NSMutableDictionary alloc] initWithCapacity:10];
}

// cleanup
- (void) dealloc
{
	[umpListener release];
	[touchTrails release];
	[touches release];
	[super dealloc];
}


# pragma mark touch accessor

// change touches
- (void)setTouches:(NSArray *)someTouches;
{
	if(touches != someTouches) {
		[touches release];
		touches = [someTouches retain];
		[self setNeedsDisplay:YES];
	}
}

# pragma mark drawing

// drawing
- (void)drawRect:(NSRect)rect;
{
	CGContextRef c = [[NSGraphicsContext currentContext] graphicsPort];
	
	// draw stuff
	if(showGrid && umpListener) [self drawGridInRect:rect context:c];
	if(showTouchTrail && touches) [self drawTouchTrailsInRect:rect context:c];
	if(showTouches && touches) [self drawTouchesInRect:rect context:c];
}

// draw the raw data
- (void)drawGridInRect:(NSRect)rect context:(CGContextRef)c;
{
	// get bounds and grid measures
	CGRect bounds = self.bounds;
	CGSize gridSize = CGSizeMake(bounds.size.width / (float)umpListener.umpc.width, bounds.size.height / (float)umpListener.umpc.height);
	
	NSUInteger i, x, y;
	CGFloat p;
	for(i = 0; i < umpListener.umpc.length; i++) {
		
		// compute coordinates
		x = i % umpListener.umpc.width;
		y = (i - x) / umpListener.umpc.width;
		
		// read grid value
		p = umpListener.umpc.data[i] / 254.0;
		
		// draw rectangle
		CGContextSetRGBFillColor(c, GRID_COLOR);
		CGContextFillRect(c, CGRectMake(bounds.origin.x + gridSize.width * x, 
										bounds.origin.y + gridSize.height * y,
										gridSize.width,
										gridSize.height));									  
	}
	
}

// draw touches
- (void)drawTouchesInRect:(NSRect)rect context:(CGContextRef)c;
{
	// get bounds and grid measures
	CGRect bounds = self.bounds;
	CGSize gridSize = CGSizeMake(bounds.size.width / (float)umpListener.umpc.width, bounds.size.height / (float)umpListener.umpc.height);
	
	UMPTouch *touch;
	CGFloat x, y;
	CGFloat px, py;
	
	// compute coordinate system modifiers
	CGFloat dx = bounds.size.width + gridSize.width * 0.5;
	CGFloat dy = bounds.size.height + gridSize.width * 0.5;
	
	// draw lines between point and previous point
	if(showPreviousTouch) {
		CGContextSetRGBStrokeColor(c, TOUCH_PREVIOUS_LINE_COLOR);
		for(touch in touches) {
			if(touch.previousPressure > 0.0) {
				CGContextMoveToPoint(c, touch.x * dx, touch.y * dy);
				CGContextAddLineToPoint(c, touch.previousPoint.x * dx, touch.previousPoint.y * dy);
				CGContextStrokePath(c);
			}
		}
	}

	// draw crosses
	CGContextSetRGBStrokeColor(c, TOUCH_CROSS_COLOR);
	for(touch in touches) {
		
		// convert coordinates to local system
		x = touch.x * dx;
		y = touch.y * dy;

		// draw cross
		CGContextMoveToPoint(c, x, y - TOUCH_CROSS_SIZE);
		CGContextAddLineToPoint(c, x, y + TOUCH_CROSS_SIZE);
		CGContextMoveToPoint(c, x - TOUCH_CROSS_SIZE, y);
		CGContextAddLineToPoint(c, x + TOUCH_CROSS_SIZE, y);
		CGContextStrokePath(c);

		// draw pressure
		if(showTouchesPressure) {
			px = x + TOUCH_PRESSURE_OFFSET;
			py = y - TOUCH_PRESSURE_OFFSET;
			CGContextSetRGBFillColor(c, TOUCH_PRESSURE_BGCOLOR);
			CGContextFillRect(c, CGRectMake(px, py, TOUCH_PRESSURE_WIDTH, TOUCH_PRESSURE_HEIGHT));
			CGContextSetRGBFillColor(c, TOUCH_PRESSURE_COLOR);
			CGContextFillRect(c, CGRectMake(px, py, TOUCH_PRESSURE_WIDTH, touch.pressure * TOUCH_PRESSURE_HEIGHT));
		}
		
	}
	
}

// draw touch trail
- (void)drawTouchTrailsInRect:(NSRect)rect context:(CGContextRef)c;
{
	NSRect bounds = self.bounds;
	CGSize gridSize = CGSizeMake(bounds.size.width / (float)umpListener.umpc.width, bounds.size.height / (float)umpListener.umpc.height);
	
	CGFloat dx = bounds.size.width + gridSize.width * 0.5;
	CGFloat dy = bounds.size.height + gridSize.width * 0.5;

	UMPTouch *touch;
	NSPoint p;
	NSBezierPath *touchTrail;
	NSNumber *key;

	// draw the touch graph
	CGContextSetRGBStrokeColor(c, TOUCH_TRAIL_COLOR);
	for(touch in touches) {
		key = [[NSNumber alloc] initWithInt:(NSInteger)touch];
		p.x = touch.point.x * dx;
		p.y = touch.point.y * dy;
		
		// create or update bezier path
		touchTrail = [touchTrails objectForKey:key];
		if(touchTrail == nil) {
			touchTrail = [NSBezierPath bezierPath];
			[touchTrails setObject:touchTrail forKey:key];
			[touchTrail moveToPoint:p];
		} else {
			[touchTrail lineToPoint:p];
			
			// stroke
			[touchTrail stroke];
		}
		[key release];
	}
	
	// clean up old touch trails
	for(touch in [touchTrails allKeys]) {
		key = [[NSNumber alloc] initWithInt:(NSInteger)touch];
		if(![touches containsObject:touch]) {
			[touchTrails removeObjectForKey:key];
			[key release];
		}
	}
}


@end
